package com.cyy.common.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author caoyunyou
 */
public class TreeUtils {

    @SuppressWarnings("unchecked")
    public static <T, E> void generatorTree(List<T> list, T currNode, Set<E> visited) {


        try {
            if (Objects.isNull(currNode)) {
                return;
            }
            // 获取节点的Clz信息
            Class<?> nodeClz = currNode.getClass();

            // 获取需要用到的字段信息，并设置可访问
            Field filedId = nodeClz.getDeclaredField("id");
            filedId.setAccessible(true);
            Field fieldParentId = nodeClz.getDeclaredField("parentId");
            fieldParentId.setAccessible(true);
            Field fieldChildren = nodeClz.getDeclaredField("children");
            fieldChildren.setAccessible(true);


            // 取出当前节点ID，加入到已访问集合中
            E currNodeId = (E) filedId.get(currNode);
            visited.add(currNodeId);

            // 遍历整个列表
            for (T forNode : list) {
                // 拿出当前遍历的节点ID
                E forNodeId = (E) filedId.get(forNode);
                // 如果当前遍历节点已访问，就跳过
                if (visited.contains(forNodeId)) {
                    continue;
                }
                // 获取遍历节点的父级ID
                Integer forNodeParentId = (Integer) fieldParentId.get(forNode);
                // 如果遍历的节点的parentId刚好是当前处理节点的ID，就加入到子级中
                if (forNodeParentId.equals(currNodeId)) {
                    List<T> children = (List<T>) fieldChildren.get(currNode);
                    // 如果当前children为空，则创建子级，要不然就不创建，这样虽然每次会进行判断，但是不会多创建子级。
                    if (Objects.isNull(children)) {
                        children = new ArrayList<>();
                        fieldChildren.set(currNode, children);
                    }
                    // 设置子级
                    children.add(forNode);
                    generatorTree(list, forNode, visited);
                }

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
